package cn.bitkit.tools.crypto;

import cn.bitkit.base.util.StringUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * 
 * @author changbo
 * 
 */
@Slf4j
public class AES {
	
	public static final String KEY_ALGORITHM = "AES";
	public static final String ECB_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
	public static final String CBC_CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";

	public static String encrypt(String data, String key) {
		return encrypt(data, key, null, ECB_CIPHER_ALGORITHM);
	}

	public static String encrypt(String data, String key, String iv) {
		return encrypt(data, key, iv, CBC_CIPHER_ALGORITHM);
	}

	public static String encrypt(String data, String key, String iv, String cipherAlgorithm) {
		byte[] ivBytes = iv == null ? null : iv.getBytes();
		byte[] res = cipher(data.getBytes(), key.getBytes(), ivBytes, cipherAlgorithm, Cipher.ENCRYPT_MODE);
		return StringUtil.encodeBase64(res);
	}

	public static String decrypt(String data, String key) {
		return decrypt(data, key, null, ECB_CIPHER_ALGORITHM);
	}

	public static String decrypt(String data, String key, String iv) {
		return decrypt(data, key, iv, CBC_CIPHER_ALGORITHM);
	}
	
	public static String decrypt(String data, String key, String iv, String cipherAlgorithm) {
		byte[] ivBytes = iv == null ? null : iv.getBytes();
		byte[] res = cipher(StringUtil.decodeBase64(data), key.getBytes(), ivBytes, cipherAlgorithm, Cipher.DECRYPT_MODE);
		return res == null ? null : new String(res);
	}

	@SneakyThrows
	public static byte[] cipher(byte[] dataBytes, byte[] keyBytes, byte[] ivBytes, String cipherAlgorithm, int mode) {
		SecretKeySpec keySpec = SecretKey.paddingByteKey(keyBytes, KEY_ALGORITHM);
		Cipher cipher = Cipher.getInstance(cipherAlgorithm);
		if(ivBytes == null){
			cipher.init(mode, keySpec);
		}else{
			IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
			cipher.init(mode, keySpec, ivSpec);
		}
		return cipher.doFinal(dataBytes);
	}


}
